using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace MIMPWinForms.SpriteDisplays
{
    public class SpriteDisplay
    {
        private Vector2 origin;
        /// <summary>
        /// A Vector2 that stores the origin (upper left pixel) of the SpriteDisplay.
        /// </summary>
        public Vector2 Origin {
            get { return origin; }
            set {
                origin = value;
                bounds = new Rectangle((int)origin.X, (int)origin.Y, (int)(sprite.Width * scale), (int)(sprite.Height * scale));
            }
        }
        private double scale;
        /// <summary>
        /// Factor by which to scale this SpriteDisplay when drawing it.
        /// </summary>
        public double Scale {
            get { return scale; }
            set {
                scale = value;
                bounds = new Rectangle((int)origin.X, (int)origin.Y, (int)(sprite.Width * scale), (int)(sprite.Height * scale));
            }
        }
        private Texture2D sprite;
        /// <summary>
        /// The Texture2D that is used to draw the Sprite.
        /// </summary>
        public Texture2D Sprite {
            get { return sprite; }
            set {
                sprite = value;
                bounds = new Rectangle((int)origin.X, (int)origin.Y, (int)(sprite.Width * scale), (int)(sprite.Height * scale));
            }
        }
        private Rectangle bounds;
        /// <summary>
        /// Gets the bounds of the SpriteDisplay. Can not be set directly--to modify, change Origin, Scale, or Sprite.
        /// </summary>
        public Rectangle Bounds {
            get { return bounds; }
        }
        private Color[] spriteData;

        private bool isMouseOver;
        /// <summary>
        /// True if the mouse cursor is hovering over this SpriteDisplay *and* IsClickable is true, false otherwise. 
        /// To set this value, use the SetIsMouseOver method.
        /// </summary>
        public bool IsMouseOver {
            get { return isClickable && isMouseOver; }
        }
        private bool isClickable;
        /// <summary>
        /// Indicates whether the user can interact with this SpriteDisplay. If false, IsMouseOver will never be true.
        /// </summary>
        public bool IsClickable {
            get { return isClickable; }
            set { isClickable = value; }
        }
        private Keys key;
        /// <summary>
        /// The keyboard key associated with this SpriteDisplay. 
        /// </summary>
        public Keys ShortcutKey {
            get { return key; }
            set { key = value; }
        }
        private bool visible;
        /// <summary>
        /// True if the SpriteDisplay should be drawn, false otherwise.
        /// </summary>
        public bool IsVisible {
            get { return visible; }
            set { visible = value; }
        }
        /// <summary>
        /// Constructs a new SpriteDisplay object.
        /// </summary>
        /// <param name="x">The x coordinate of the origin of the display.</param>
        /// <param name="y">The y coordinate of the origin of the display.</param>
        /// <param name="scale">The factor by which to scale the display.</param>
        /// <param name="key">The keyboard key associated with this SpriteDisplay.</param>
        /// <param name="sprite">The Texture2D used to draw the display.</param>
        /// <param name="isClickable">True if the SpriteDisplay can be clicked on by the user.</param>
        /// <param name="isVisible">True if the SpriteDisplay should be drawn.</param>
        public SpriteDisplay(float x, float y, float scale, Keys key, Texture2D sprite, bool isVisible, bool isClickable) {
            this.scale = scale;
            this.origin = new Vector2(x, y);
            this.isMouseOver = false;
            this.isClickable = true;
            this.sprite = sprite;
            this.key = key;
            this.spriteData = new Color[sprite.Width * sprite.Height];
            this.sprite.GetData<Color>(this.spriteData);
            this.bounds = new Rectangle((int)origin.X, (int)origin.Y, (int)(sprite.Width * scale), (int)(sprite.Height * scale));
            this.visible = isVisible;
            this.isClickable = isClickable;
        }

        public SpriteDisplay(float x, float y, float scale, Texture2D sprite)
            : this(x, y, scale, Keys.D9, sprite) { }

        public SpriteDisplay(float x, float y, float scale, Keys key, Texture2D sprite)
            : this(x, y, scale, key, sprite, true, true) { }

        /// <summary>
        /// Sets the IsMouseOver property. Uses the cursor location in the given 
        /// MouseState to determine if the cursor is hovering over the SpriteDisplay.
        /// </summary>
        /// <param name="state">The current mouse state; used to determine the cursor location.</param>
        public void SetIsMouseOver(MouseState state) {
            SetIsMouseOver(state.X, state.Y);
        }

        /// <summary>
        /// Sets the IsMouseOver property. Uses the cursor location specified by x and y
        /// to determine if the cursor is hovering over the SpriteDisplay.
        /// </summary>
        /// <param name="x">X coordinate of the current cursor position</param>
        /// <param name="y">Y coordinate of the current cursor position</param>
        public void SetIsMouseOver(int x, int y) {
            bool inBoundingBox = x >= origin.X && x < origin.X + sprite.Width &&
                                 y >= origin.Y && y < origin.Y + sprite.Height;

            Color c = Color.White;
            if (inBoundingBox) {
                //get the pixel that the cursor is over by finding x and y relative to the SpriteDisplay origin
                int xpos = x - (int)origin.X;
                int ypos = y - (int)origin.Y;
                c = spriteData[(xpos + ypos * sprite.Width)];
            }
            isMouseOver = inBoundingBox && c.A > 20;
        }

        /// <summary>
        /// Checks if the whole Sprite is displayed within the (inclusive) bounds of the given Rectangle.
        /// </summary>
        /// <param name="bounds">The Rectangle to check the Sprite's location against.</param>
        /// <returns>True if all the texels of the Sprite are located inside the bounds of the given Rectangle.</returns>
        public bool IsInRectangle(Rectangle bounds) {
            return origin.X >= bounds.Left && origin.X + sprite.Width < bounds.Right &&
                origin.Y >= bounds.Top && origin.Y + sprite.Height < bounds.Bottom;
        }
        /// <summary>
        /// Searches each pixel in this SpriteDisplay's sprite and replaces all occurances of one Color
        /// with another Color.
        /// </summary>
        /// <param name="toReplace">The Color to search for in the sprite.</param>
        /// <param name="newColor">The Color to replace the found color with.</param>
        public void ReplaceColorInSprite(Color toReplace, Color newColor) {
            for (int i = 0; i < spriteData.Length; i++) {
                if (spriteData[i].Equals(toReplace)) {
                    spriteData[i] = newColor;
                }
            }
            sprite.SetData<Color>(spriteData);
        }

        /// <summary>
        /// Draws the SpriteDisplay's sprite using the given SpriteBatch.
        /// </summary>
        /// <param name="spriteBatch">Used to draw the SpriteDisplay's sprite.</param>
        public virtual void Draw(SpriteBatch spriteBatch) {
            spriteBatch.Draw(sprite, bounds, Color.White);
        }
    }
}
